package db_config

import (
    "errors"

    "go.uber.org/zap"
    "gorm.io/gorm"
    "libgo/storage/csv_rows"
    "libgo/storage/struct_config"
)

type DBConfig struct {
    logger *zap.Logger
    db     *gorm.DB
    cfg    *Config
}

func New() *DBConfig {
    x := &DBConfig{}
    x.cfg = DefaultConfig()
    return x
}

func (s *DBConfig) WithLogger(l *zap.Logger) *DBConfig {
    s.logger = l
    return s
}

func (s *DBConfig) WithDB(db *gorm.DB) *DBConfig {
    s.db = db
    return s
}

func (s *DBConfig) WithConfig(cfg *Config) *DBConfig {
    s.cfg = cfg
    return s
}

func (s *DBConfig) WithInitCSV(csvfile string) *DBConfig {
    s.cfg.InitCSV = csvfile
    return s
}

func (s *DBConfig) Start() error {
    table_list := []interface{}{
        SysConfig{},
    }
    var need_migrate bool
    tx := s.db.Session(&gorm.Session{})

    if !tx.Migrator().HasTable(&SysConfig{}) {
        need_migrate = true
    }

    if need_migrate {

        if err := s.db.AutoMigrate(table_list...); err != nil {
            return err
        }

        s.init_from_csv(s.cfg.InitCSV)
    }
    return nil
}

func (s *DBConfig) init_from_csv(fname string) {
    logger := s.logger.Sugar()

    var lst []SysConfig
    if err := csv_rows.LoadFromFile(fname, &lst); err != nil {
        logger.Error("加载", fname, "失败:", err)
        return
    } else {
        logger.Info("加载", fname, "成功")
        tx := s.db.Begin()
        for _, def := range lst {
            logger.Info(def)
            if err := tx.Create(&def).Error; err != nil {
                logger.Error(err)
            }
        }
        tx.Commit()
    }
}

func (s *DBConfig) Read(group string, ptr_struct interface{}) error {
    return struct_config.ConfigToStruct(func(group string, names []string) ([]string, error) {
        var values []string
        for _, name := range names {
            cfg := &SysConfig{}
            q := s.db.Where(`"group" = ? AND "name" = ?`, group, name).First(&cfg)
            if q.Error == nil {
                values = append(values, cfg.Value)
                continue
            }
            if errors.Is(q.Error, gorm.ErrRecordNotFound) {
                values = append(values, "")
                continue
            }
            return nil, q.Error
        }
        return values, nil
    }, group, ptr_struct)
}

func (s *DBConfig) Update(group string, ptr_struct interface{}) error {
    return struct_config.StructToConfig(func(group string, names, values []string) error {
        tx := s.db.Begin()
        for i := 0; i < len(names); i++ {
            name := names[i]
            value := values[i]
            if err := tx.Model(&SysConfig{}).Where(`"group" = ? AND "name" = ?`, group, name).Update("value", value).Error; err != nil {
                tx.Rollback()
                return err
            }
        }
        if err := tx.Commit().Error; err != nil {
            tx.Rollback()
            return err
        }
        return nil
    }, group, ptr_struct)
}
